home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / gnucdiff / diff.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-12-20  |  14.6 KB  |  573 lines

  1. /* gnu diff main routine.
  2.    Copyright (C) 1988 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY.  No author or distributor
  8. accepts responsibility to anyone for the consequences of using it
  9. or for whether it serves any particular purpose or works at all,
  10. unless he says so in writing.  Refer to the GNU DIFF General Public
  11. License for full details.
  12.  
  13. Everyone is granted permission to copy, modify and redistribute
  14. GNU DIFF, but only under the conditions described in the
  15. GNU DIFF General Public License.   A copy of this license is
  16. supposed to have been given to you along with GNU DIFF so you
  17. can know your rights and responsibilities.  It should be in a
  18. file named COPYING.  Among other things, the copyright notice
  19. and this notice must be preserved on all copies.  */
  20.  
  21. /* GNU DIFF was written by Mike Haertel, David Hayes,
  22.    Richard Stallman and Len Tower.  */
  23.  
  24. #define GDIFF_MAIN
  25. #include "regex.h"
  26. #include "diff.h"
  27.  
  28.  
  29. /* Nonzero for -r: if comparing two directories,
  30.    compare their common subdirectories recursively.  */
  31.  
  32. int recursive;
  33.  
  34. /* For debugging: don't do discard_confusing_lines.  */
  35.  
  36. int no_discards;
  37.  
  38. /* Return a string containing the command options with which diff was invoked.
  39.    Spaces appear between what were separate ARGV-elements.
  40.    There is a space at the beginning but none at the end.
  41.    If there were no options, the result is an empty string.
  42.  
  43.    Arguments: VECTOR, a vector containing separate ARGV-elements, and COUNT,
  44.    the length of that vector.  */
  45.  
  46. static char *
  47. option_list (vector, count)
  48.      char **vector;
  49.      int count;
  50. {
  51.   int i;
  52.   int length = 0;
  53.   char *result;
  54.  
  55.   for (i = 0; i < count; i++)
  56.     length += strlen (vector[i]) + 1;
  57.  
  58.   result = (char *) xmalloc (length + 1);
  59.   result[0] = 0;
  60.  
  61.   for (i = 0; i < count; i++)
  62.     {
  63.       strcat (result, " ");
  64.       strcat (result, vector[i]);
  65.     }
  66.  
  67.   return result;
  68. }
  69.  
  70. void main (argc, argv)
  71.      int argc;
  72.      char *argv[];
  73. {
  74.   int i;
  75.   int val;
  76.   int c;
  77.  
  78.   extern int optind;
  79.   extern char *optarg;
  80.   extern char *basename();
  81.  
  82.   program = basename(argv[0]);
  83.  
  84.   /* Do our initializations. */
  85.   output_style = OUTPUT_NORMAL;
  86.   always_text_flag = FALSE;
  87.   ignore_space_change_flag = FALSE;
  88.   ignore_all_space_flag = FALSE;
  89.   length_varies = FALSE;
  90.   ignore_case_flag = FALSE;
  91.   ignore_blank_lines_flag = FALSE;
  92.   ignore_regexp = 0;
  93.   function_regexp = 0;
  94.   print_file_same_flag = FALSE;
  95.   entire_new_file_flag = FALSE;
  96.   context = -1;
  97.   line_end_char = '\n';
  98.   tab_align_flag = FALSE;
  99.   tab_expand_flag = FALSE;
  100.   recursive = FALSE;
  101.   paginate_flag = FALSE;
  102.   heuristic = FALSE;
  103.   dir_start_file = NULL;
  104.   msg_chain = NULL;
  105.   msg_chain_end = NULL;
  106.   no_discards = 0;
  107.  
  108.   /* Decode the options.  */
  109.  
  110.   while ((c = getopt (argc, argv, "0123456789abBcCdefF:hHiI:lnNrsS:tTw"))
  111.      != EOF)
  112.     switch (c)
  113.       {
  114.     /* All digits combine in decimal to specify the context-size.  */
  115.       case '1':
  116.       case '2':
  117.       case '3':
  118.       case '4':
  119.       case '5':
  120.       case '6':
  121.       case '7':
  122.       case '8':
  123.       case '9':
  124.       case '0':
  125.     if (context == -1)
  126.       context = 0;
  127.     context= context * 10 + c - '0';
  128.     break;
  129.  
  130.       case 'a':
  131.     /* Treat all files as text files; never treat as binary.  */
  132.     always_text_flag = 1;
  133.     break;
  134.  
  135.       case 'b':
  136.     /* Ignore changes in amount of whitespace.  */
  137.     ignore_space_change_flag = 1;
  138.     length_varies = 1;
  139.     break;
  140.  
  141.       case 'B':
  142.     /* Ignore changes affecting only blank lines.  */
  143.     ignore_blank_lines_flag = 1;
  144.     break;
  145.  
  146.       case 'c':
  147.     /* Make context-style output.  */
  148.     specify_style (OUTPUT_CONTEXT);
  149.     break;
  150.  
  151.       case 'C':
  152.     /* Make context-style output and show name of last C function.  */
  153.     specify_style (OUTPUT_CONTEXT);
  154.     function_regexp = "^[_a-zA-Z]";
  155.     break;
  156.  
  157.       case 'd':
  158.     /* Don't discard lines.  This makes things slower (sometimes much
  159.        slower) but will find a guaranteed minimal set of changes.  */
  160.     no_discards = 1;
  161.     break;
  162.  
  163.       case 'e':
  164.     /* Make output that is a valid `ed' script.  */
  165.     specify_style (OUTPUT_ED);
  166.     break;
  167.  
  168.       case 'f':
  169.     /* Make output that looks vaguely like an `ed' script
  170.        but has changes in the order they appear in the file.  */
  171.     specify_style (OUTPUT_FORWARD_ED);
  172.     break;
  173.  
  174.       case 'F':
  175.     /* Show, for each set of changes, the previous line that
  176.        matches the specified regexp.  Currently affects only
  177.        context-style output.  */
  178.     function_regexp = optarg;
  179.     break;
  180.  
  181.       case 'h':
  182.     /* Split the files into chunks of around 1500 lines
  183.        for faster processing.  Usually does not change the result.
  184.  
  185.        This currently has no effect.  */
  186.     break;
  187.  
  188.       case 'H':
  189.     heuristic = 1;
  190.     break;
  191.  
  192.       case 'i':
  193.     /* Ignore changes in case.  */
  194.     ignore_case_flag = 1;
  195.     break;
  196.  
  197.       case 'I':
  198.     /* Ignore changes affecting only lines that match the
  199.        specified regexp.  */
  200.     ignore_regexp = optarg;
  201.     break;
  202.  
  203.       case 'l':
  204.     /* Pass the output through `pr' to paginate it.  */
  205.     paginate_flag = 1;
  206.     break;
  207.  
  208.       case 'n':
  209.     /* Output RCS-style diffs, like `-f' except that each command
  210.        specifies the number of lines affected.  */
  211.     specify_style (OUTPUT_RCS);
  212.     break;
  213.  
  214.       case 'N':
  215.     /* When comparing directories, if a file appears only in one
  216.        directory, treat it as present but empty in the other.  */
  217.     entire_new_file_flag = 1;
  218.     break;
  219.  
  220.       case 'r':
  221.     /* When comparing directories, 
  222.        recursively compare any subdirectories found.  */
  223.     recursive = 1;
  224.     break;
  225.  
  226.       case 's':
  227.     /* Print a message if the files are the same.  */
  228.     print_file_same_flag = 1;
  229.     break;
  230.  
  231.       case 'S':
  232.     /* When comparing directories, start with the specified
  233.        file name.  This is used for resuming an aborted comparison.  */
  234.     dir_start_file = optarg;
  235.     break;
  236.       
  237.       case 't':
  238.     /* Expand tabs to spaces in the output so that it preserves
  239.        the alignment of the input files.  */
  240.     tab_expand_flag = 1;
  241.     break;
  242.  
  243.       case 'T':
  244.     /* Use a tab in the output, rather than a space, before the
  245.        text of an input line, so as to keep the proper alignment
  246.        in the input line without changing the characters in it.  */
  247.     tab_align_flag = 1;
  248.     break;
  249.  
  250.       case 'w':
  251.     /* Ignore horizontal whitespace when comparing lines.  */
  252.     ignore_all_space_flag = 1;
  253.     length_varies = 1;
  254.     break;
  255.       }
  256.  
  257.   if (optind != argc - 2)
  258.     fatal ("requires two file names.  Usage: diff [-options] file1 file2");
  259.  
  260.   /*
  261.    * @@ need more complicated usage string for directory options??
  262.    * Note three liner at top of BSD documentation, and John Gilmore
  263.    * message in his public domain tar being used by GNU.  
  264.    */
  265.  
  266.   if (ignore_regexp)
  267.     {
  268.       char *val;
  269.       bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled);
  270.       val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp),
  271.                 &ignore_regexp_compiled);
  272.       if (val != 0)
  273.     error ("-I option: ", val);
  274.       ignore_regexp_compiled.fastmap = (char *) xmalloc (256);
  275.     }
  276.  
  277.   if (function_regexp)
  278.     {
  279.       char *val;
  280.       bzero (&function_regexp_compiled, sizeof function_regexp_compiled);
  281.       val = re_compile_pattern (function_regexp, strlen (function_regexp),
  282.                 &function_regexp_compiled);
  283.       if (val != 0)
  284.     error ("-F option: ", val);
  285.       function_regexp_compiled.fastmap = (char *) xmalloc (256);
  286.     }
  287.  
  288.   if (output_style != OUTPUT_CONTEXT)
  289.     context = 0;
  290.   else if (context == -1)
  291.     /* Default amount of context for -c.  */
  292.     context = 3;
  293.  
  294.   switch_string = option_list (argv + 1, optind - 1);
  295.  
  296.   val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
  297.  
  298.   /* Print any messages that were saved up for last.  */
  299.   print_message_queue ();
  300.  
  301.   exit (val);
  302. }
  303.  
  304. void
  305. specify_style (style)
  306.      enum output_style style;
  307. {
  308.   if (output_style != OUTPUT_NORMAL
  309.       && output_style != style)
  310.     error ("conflicting specifications of output style");
  311.   output_style = style;
  312. }
  313.  
  314. /* Compare two files (or dirs) with specified names
  315.    DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
  316.    (if DIR0 is 0, then the name is just NAME0, etc.)
  317.    This is self-contained; it opens the files and closes them.
  318.  
  319.    Value is 0 if files are identical, 1 if different,
  320.    2 if there is a problem opening them.  */
  321.  
  322. int
  323. compare_files (dir0, name0, dir1, name1, depth)
  324.      char *dir0, *dir1;
  325.      char *name0, *name1;
  326.      int depth;
  327. {
  328.   struct file_data inf[2];
  329.   register int i;
  330.   int val;
  331.   int errorcount = 0;
  332.   int stat_result[2];
  333.  
  334.   /* If this is directory comparison, perhaps we have a file
  335.      that exists only in one of the directories.
  336.      If so, just print a message to that effect.  */
  337.  
  338.   if (! entire_new_file_flag && (name0 == 0 || name1 == 0))
  339.     {
  340.       char *name = name0 == 0 ? name1 : name0;
  341.       char *dir = name0 == 0 ? dir1 : dir0;
  342.       message ("Only in %s: %s\n", dir, name);
  343.       /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
  344.       return 1;
  345.     }
  346.  
  347.   /* Mark any nonexistent file with -1 in the desc field.  */
  348.  
  349.   inf[0].desc = name0 == 0 ? -1 : 0;
  350.   inf[1].desc = name1 == 0 ? -1 : 0;
  351.  
  352.   /* Now record the full name of each file, including nonexistent ones.  */
  353.  
  354.   if (name0 == 0)
  355.     name0 = name1;
  356.   if (name1 == 0)
  357.     name1 = name0;
  358.  
  359.   inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
  360.   inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
  361.  
  362.   /* Stat the files.  Record whether they are directories.
  363.      Record in stat_result whether stat fails.  */
  364.  
  365.   for (i = 0; i <= 1; i++)
  366.     {
  367.       inf[i].stat.st_size = 0;
  368.       inf[i].stat.st_mtime = 0;
  369.       inf[i].dir_p = 0;
  370.       stat_result[i] = 0;
  371.  
  372.       if (inf[i].desc != -1
  373.       && strcmp (inf[i].name, "-"))
  374.     {
  375.       char *filename = inf[i].name;
  376.  
  377.       stat_result[i] = stat (filename, &inf[i].stat);
  378.       if (stat_result[i] < 0)
  379.         {
  380.           perror_with_name (filename);
  381.           errorcount = 1;
  382.         }
  383.       else
  384.         inf[i].dir_p = (S_IFDIR == (inf[i].stat.st_mode & S_IFDIR));
  385.     }
  386.     }
  387. #if !defined(MSDOS)
  388.     /*
  389.     ** this stuff is real bad idea under MSDOS, at least for MSC 5.1
  390.     ** because the st_ino and st_dev fields are not supported by
  391.     ** MSDOS, and so stat sets them to zero; therefore
  392.     ** this test always succeeds.
  393.     */
  394.   /* See if the two named files are actually the same physical file.
  395.      If so, we know they are identical without actually reading them.  */
  396.  
  397.   if (inf[0].stat.st_ino == inf[1].stat.st_ino
  398.       && inf[0].stat.st_dev == inf[1].stat.st_dev
  399.       && stat_result[0] == 0
  400.       && stat_result[1] == 0)
  401.     {
  402.       val = 0;
  403.       goto done;
  404.     }
  405. #endif    /* MSDOS */
  406.  
  407.   /* Open the files and record their descriptors.  */
  408.  
  409.   for (i = 0; i <= 1; i++)
  410.     {
  411.       if (inf[i].desc == -1)
  412.     ;
  413.       else if (!strcmp (inf[i].name, "-"))
  414.     {
  415.       inf[i].desc = 0;
  416.       inf[i].name = "Standard Input";
  417.     }
  418.       /* Don't bother opening if stat already failed.  */
  419.       else if (stat_result[i] == 0)
  420.     {
  421.       char *filename = inf[i].name;
  422. #if !defined(MSDOS)
  423.       inf[i].desc = open (filename, O_RDONLY, 0);
  424.       if (0 > inf[i].desc)
  425.         {
  426.           perror_with_name (filename);
  427.           errorcount = 1;
  428.         }
  429. #else
  430.     if(inf[i].dir_p == 0) {
  431.       inf[i].desc = open (filename, O_RDONLY, 0);
  432.       if (0 > inf[i].desc)
  433.         {
  434.           perror_with_name (filename);
  435.           errorcount = 1;
  436.         }
  437.         } else
  438.             inf[i].desc = 0;
  439. #endif    /* MSDOS */
  440.     }
  441.     }
  442.  
  443.   if (name0 == 0)
  444.     inf[0].dir_p = inf[1].dir_p;
  445.   if (name1 == 0)
  446.     inf[1].dir_p = inf[0].dir_p;
  447.  
  448.   if (errorcount)
  449.     {
  450.  
  451.       /* If either file should exist but fails to be opened, return 2.  */
  452.  
  453.       val = 2;
  454.  
  455.     }
  456.   else if (inf[0].dir_p && inf[1].dir_p)
  457.     {
  458.  
  459.       /* If both are directories, compare the files in them.  */
  460.  
  461.       if (depth > 0 && !recursive)
  462.     {
  463.       /* But don't compare dir contents one level down
  464.          unless -r was specified.  */
  465.       message ("Common subdirectories %s and %s\n",
  466.            inf[0].name, inf[1].name);
  467.       val = 0;
  468.     }
  469.       else
  470.     {
  471.       /* This is almost redundant; by closing sooner, we
  472.          avoid any danger of running out of descriptors
  473.          on deep recursion.  */
  474.       close (inf[0].desc);
  475.       close (inf[1].desc);
  476.       val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth);
  477.     }
  478.  
  479.     }
  480.   else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p))
  481.     {
  482.  
  483.       /* If only one is a directory, and it was specified in the command line,
  484.      use the file in that dir whose name matches the other file.  */
  485.  
  486.       int dir_arg = (inf[0].dir_p ? 0 : 1);
  487.       int fnm_arg = (inf[0].dir_p ? 1 : 0);
  488.       char *filename = concat (inf[dir_arg].name, "/", inf[fnm_arg].name);
  489.  
  490.       inf[dir_arg].desc = open (filename, O_RDONLY, 0);
  491.  
  492.       if (0 > inf[dir_arg].desc)
  493.     {
  494.       perror_with_name (filename);
  495.       val = 2;
  496.     }
  497.       else
  498.     {
  499. /*      free (inf[dir_arg].name); */ /* Commented out per DOS bug report */
  500.       inf[dir_arg].name = filename;
  501.       if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0)
  502.         pfatal_with_name (inf[dir_arg].name);
  503.  
  504.       inf[dir_arg].dir_p
  505.         = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFDIR));
  506.       if (inf[dir_arg].dir_p)
  507.         {
  508.           error ("%s is a directory but %s is not",
  509.              inf[dir_arg].name, inf[fnm_arg].name);
  510.           val = 2;
  511.         }
  512.       else
  513.         val = diff_2_files (inf, depth);
  514.     }
  515.  
  516.     }
  517.   else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p))
  518.     {
  519.       /* Perhaps we have a subdirectory that exists only in one directory.
  520.      If so, just print a message to that effect.  */
  521.  
  522.       if (inf[0].desc == -1 || inf[1].desc == -1)
  523.     {
  524.       char *dir = (inf[0].desc == -1) ? dir1 : dir0;
  525.       message ("Only in %s: %s\n", dir, name0);
  526.     }
  527.       else
  528.     {
  529.       /* We have a subdirectory in one directory
  530.          and a file in the other.  */
  531.  
  532.       if (inf[0].dir_p)
  533.         message ("%s is a directory but %s is not\n",
  534.              inf[0].name, inf[1].name);
  535.       else
  536.         message ("%s is a directory but %s is not\n",
  537.              inf[1].name, inf[0].name);
  538.     }
  539.  
  540.       val = 2;
  541.     }
  542.   else
  543.     {
  544.  
  545.       /* Both exist and both are ordinary files.  */
  546.  
  547.       val = diff_2_files (inf, depth);
  548.  
  549.     }
  550.  
  551.   /* Now the comparison has been done, if no error prevented it,
  552.      and VAL is the value this function will return.  */
  553.  
  554.   if (inf[0].desc > 0)
  555.     close (inf[0].desc);
  556.   if (inf[1].desc > 0)
  557.     close (inf[1].desc);
  558.  
  559.  done:
  560.   if (val == 0 && print_file_same_flag)
  561.     message ("Files %s and %s are identical\n",
  562.          inf[0].name, inf[1].name);
  563.  
  564.   fflush (stdout);
  565.  
  566.   if (dir0 != 0)
  567.     free (inf[0].name);
  568.   if (dir1 != 0)
  569.     free (inf[1].name);
  570.  
  571.   return val;
  572. }
  573.